package com.api.client.impl;

import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URI;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.client.utils.URLEncodedUtils;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.InputStreamBody;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import com.api.client.model.ApiRequest;
import com.api.client.model.ApiResponse;

public class ApiMultipartRequest implements ApiRequest {

	private final String method;

	private final String url;

	private final Map<String, String> headers = new HashMap<String, String>();

	private final Map<String, String> queryParams = new HashMap<String, String>();

	private final Map<String, String> textFormParams = new HashMap<String, String>();

	private final Map<String, FileInputStream> fileFormParams = new HashMap<String, FileInputStream>();

	public ApiMultipartRequest(String method, String url) {

		if (method == null || url == null) {
			throw new IllegalArgumentException("Method and url cannot be null");
		}

		if (!method.equals("POST") && !method.equals("PUT")) {
			throw new IllegalArgumentException(String.format("Method %s multipart not supported", method));
		}

		this.method = method;
		this.url = url;
	}

	@Override
	public String getMethod() {
		return method;
	}

	@Override
	public String getUrl() {
		return url;
	}

	@Override
	public Map<String, String> getHeaders() {
		return Collections.unmodifiableMap(headers);
	}

	@Override
	public Map<String, String> getQueryParams() {
		return Collections.unmodifiableMap(queryParams);
	}

	@Override
	public Map<String, Object> getFormParams() {
		Map<String, Object> map = new HashMap<String, Object>();

		map.putAll(textFormParams);
		map.putAll(fileFormParams);

		return map;
	}

	public void addQueryValues(Map<String, String> values) {
		queryParams.putAll(values);
	}

	public void addTextFormValues(Map<String, String> values) {
		textFormParams.putAll(values);
	}

	public void addFileFormValues(Map<String, FileInputStream> values) {
		fileFormParams.putAll(values);
	}

	@Override
	public ApiResponse send() {

		// HttpParams my_httpParams = new BasicHttpParams();
		// HttpConnectionParams.setConnectionTimeout(my_httpParams, connection_Timeout);
		// HttpConnectionParams.setSoTimeout(my_httpParams, connection_Timeout);
		// HttpClient httpclient = new DefaultHttpClient(my_httpParams);
		HttpClient httpclient = new DefaultHttpClient();
		try {

			List<NameValuePair> qparams = new ArrayList<NameValuePair>();

			for (Map.Entry<String, String> query : queryParams.entrySet()) {

				qparams.add(new BasicNameValuePair(query.getKey(), query.getValue()));
			}
			URI uri = URI.create(url + "?" + URLEncodedUtils.format(qparams, "UTF-8"));

			MultipartEntity reqEntity = new MultipartEntity();

			for (Map.Entry<String, String> param : textFormParams.entrySet()) {

				StringBody str = new StringBody(param.getValue());

				reqEntity.addPart(param.getKey(), str);
			}

			for (Map.Entry<String, FileInputStream> param : fileFormParams.entrySet()) {

				InputStreamBody uploadFilePart = new InputStreamBody(param.getValue(), "file");

				reqEntity.addPart(param.getKey(), uploadFilePart);
			}

			HttpEntityEnclosingRequestBase req = method.equals("POST") ? new HttpPost(uri) : new HttpPut(uri);

			for (Entry<String, String> headerEntry : headers.entrySet()) {
				req.addHeader(headerEntry.getKey(), headerEntry.getValue());
			}

			req.setEntity(reqEntity);

			RequestLogger.logRequest(this);

			HttpResponse response = httpclient.execute(req);

			InputStream content = response.getEntity().getContent();

			ApiResponse apiResponse = new ApiResponseImpl(method, url, response.getStatusLine().getStatusCode(),
					EntityUtils.toString(response.getEntity()), content);

			RequestLogger.logResponse(apiResponse);
			return apiResponse;

		} catch (Exception e) {

			throw new IllegalStateException("Problems sending multipart request", e);
		}
	}

	public void addHeaderValues(Map<String, String> values) {
		headers.putAll(values);
	}

	@Override
	public String getHeader(String name) {
		if (!headers.containsKey(name)) {
			throw new IllegalArgumentException(String.format("Header '%s' doesn't exist in request", name));
		}
		return headers.get(name);
	}
}
